home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 51 / Amiga Format CD51 (2000-03-10)(Future Publishing)(GB)[!][issue 2000-04].iso / -in_the_mag- / workbench / term_4.8 / extras / source / term-source.lha / Accountant.c < prev    next >
C/C++ Source or Header  |  1997-03-11  |  10KB  |  509 lines

  1. /*
  2. **    Accountant.c
  3. **
  4. **    Connection cost accounting
  5. **
  6. **    Copyright © 1990-1997 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. **
  9. **    :ts=4
  10. */
  11.  
  12. #ifndef _GLOBAL_H
  13. #include "Global.h"
  14. #endif
  15.  
  16. /****************************************************************************/
  17.  
  18. enum    { STATE_Idle, STATE_Waiting };
  19.  
  20. /****************************************************************************/
  21.  
  22. enum    { ACMSG_Start,ACMSG_Stop,ACMSG_Query };
  23.  
  24. typedef struct AccountantMsg
  25. {
  26.     struct Message    Message;
  27.  
  28.     UWORD            Type;
  29.     struct timeval    Time;
  30. } AccountantMsg;
  31.  
  32. /****************************************************************************/
  33.  
  34. STATIC struct MsgPort            *AccountantPort;
  35. STATIC struct Task                *AccountantTask;
  36.  
  37. STATIC struct SignalSemaphore     AccountantSemaphore;
  38. STATIC ULONG                     AccountantCost;
  39.  
  40. /****************************************************************************/
  41.  
  42.     /* AccountantEntry(VOID):
  43.      *
  44.      *    The rates accounting task.
  45.      */
  46.  
  47. STATIC VOID SAVE_DS
  48. AccountantEntry(VOID)
  49. {
  50.     struct timerequest *TimeRequest;
  51.     struct MsgPort *TimePort;
  52.  
  53.         /* Set up the timer resources */
  54.  
  55.     TimeRequest    = NULL;
  56.     TimePort    = NULL;
  57.  
  58.     if(CreateTimeRequest(UNIT_VBLANK,&TimeRequest,&TimePort))
  59.     {
  60.             /* Now create the communications port */
  61.  
  62.         if(AccountantPort = CreateMsgPort())
  63.         {
  64.             struct timeval StopTime;
  65.             struct timeval Now;
  66.             ULONG Signals;
  67.             WORD State;
  68.  
  69.                 /* That's me */
  70.  
  71.             AccountantTask = FindTask(NULL);
  72.  
  73.                 /* Wake up the main program */
  74.  
  75.             Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
  76.  
  77.                 /* Doing nothing yet... */
  78.  
  79.             State = STATE_Idle;
  80.  
  81.             FOREVER
  82.             {
  83.                     /* Wait for a sign */
  84.  
  85.                 Signals = Wait(PORTMASK(TimePort) | PORTMASK(AccountantPort) | SIG_KILL);
  86.  
  87.                     /* Terminate this task? */
  88.  
  89.                 if(Signals & SIG_KILL)
  90.                     break;
  91.  
  92.                     /* The timer has elapsed? */
  93.  
  94.                 if((Signals & PORTMASK(TimePort)) && State == STATE_Waiting)
  95.                 {
  96.                     PhoneEntry *ChosenEntry;
  97.                     struct List *ChosenPattern;
  98.                     ULONG Cost;
  99.  
  100.                         /* Terminate the timer request */
  101.  
  102.                     WaitIO((struct IORequest *)TimeRequest);
  103.  
  104.                         /* We are no longer waiting */
  105.  
  106.                     State = STATE_Idle;
  107.  
  108.                         /* Start with a clean slate */
  109.  
  110.                     Cost = 0;
  111.  
  112.                         /* We want to read the accounting data */
  113.  
  114.                     ChosenPattern = LockActivePattern();
  115.                     ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
  116.  
  117.                     if(ChosenEntry || ChosenPattern)
  118.                     {
  119.                             /* Look up the current time */
  120.  
  121.                         GetSysTime(&Now);
  122.  
  123.                             /* Check which rate data is current */
  124.  
  125.                         SelectTime(ChosenEntry,ChosenPattern,&Now);
  126.  
  127.                             /* Store the cost for the next unit */
  128.  
  129.                         Cost = PayPerUnit[DT_NEXT_UNIT];
  130.  
  131.                             /* Does this unit last for a certain time? */
  132.  
  133.                         if(SecPerUnit[DT_NEXT_UNIT] > 0)
  134.                         {
  135.                             struct timeval TimeVal;
  136.  
  137.                                 /* This is the time it takes for the */
  138.                                 /* unit to elapse */
  139.  
  140.                             TimeVal.tv_secs        = SecPerUnit[DT_NEXT_UNIT] / 10000;
  141.                             TimeVal.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  142.  
  143.                                 /* Remember when the unit will have elapsed */
  144.  
  145.                             GetSysTime(&StopTime);
  146.                             AddTime(&StopTime,&TimeVal);
  147.  
  148.                                 /* Start waiting again */
  149.  
  150.                             TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  151.                             TimeRequest->tr_time            = TimeVal;
  152.  
  153.                             SendIO((struct IORequest *)TimeRequest);
  154.  
  155.                             State = STATE_Waiting;
  156.                         }
  157.                     }
  158.  
  159.                     UnlockActiveEntry(GlobalPhoneHandle);
  160.                     UnlockActivePattern();
  161.  
  162.                         /* Add up the connection cost */
  163.  
  164.                     ObtainSemaphore(&AccountantSemaphore);
  165.                     AccountantCost += Cost;
  166.                     ReleaseSemaphore(&AccountantSemaphore);
  167.                 }
  168.  
  169.                     /* Somebody wants us to do something... */
  170.  
  171.                 if(Signals & PORTMASK(AccountantPort))
  172.                 {
  173.                     AccountantMsg *Message;
  174.  
  175.                         /* For each message... */
  176.  
  177.                     while(Message = (AccountantMsg *)GetMsg(AccountantPort))
  178.                     {
  179.                         switch(Message->Type)
  180.                         {
  181.                                 /* Start waiting? */
  182.  
  183.                             case ACMSG_Start:
  184.  
  185.                                     /* Remember the time when this unit */
  186.                                     /* will have elapsed */
  187.  
  188.                                 GetSysTime(&StopTime);
  189.                                 AddTime(&StopTime,&Message->Time);
  190.  
  191.                                     /* Start waiting */
  192.  
  193.                                 TimeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  194.                                 TimeRequest->tr_time            = Message->Time;
  195.  
  196.                                 SendIO((struct IORequest *)TimeRequest);
  197.  
  198.                                 State = STATE_Waiting;
  199.  
  200.                                 break;
  201.  
  202.                                 /* Stop waiting? */
  203.  
  204.                             case ACMSG_Stop:
  205.  
  206.                                     /* Are we currently waiting? */
  207.  
  208.                                 if(State == STATE_Waiting)
  209.                                 {
  210.                                     if(!CheckIO((struct IORequest *)TimeRequest))
  211.                                         AbortIO((struct IORequest *)TimeRequest);
  212.  
  213.                                     WaitIO((struct IORequest *)TimeRequest);
  214.  
  215.                                     State = STATE_Idle;
  216.                                 }
  217.  
  218.                                 break;
  219.  
  220.                                 /* Return the time that still has to elapse? */
  221.  
  222.                             case ACMSG_Query:
  223.  
  224.                                 if(State == STATE_Waiting)
  225.                                 {
  226.                                         /* This is now */
  227.  
  228.                                     GetSysTime(&Now);
  229.  
  230.                                         /* Is there still time left */
  231.                                         /* before the unit elapses? */
  232.  
  233.                                     if(CmpTime(&StopTime,&Now) < 0)    /* i.e. "StopTime > Now" */
  234.                                     {
  235.                                             /* Return the remaining time */
  236.  
  237.                                         Message->Time = StopTime;
  238.  
  239.                                         SubTime(&Message->Time,&Now);
  240.  
  241.                                         break;
  242.                                     }
  243.                                 }
  244.  
  245.                                     /* Clear the time value */
  246.  
  247.                                 memset(&Message->Time,0,sizeof(Message->Time));
  248.  
  249.                                 break;
  250.                         }
  251.  
  252.                             /* Return the message */
  253.  
  254.                         ReplyMsg((struct Message *)Message);
  255.                     }
  256.                 }
  257.             }
  258.  
  259.                 /* Are we still waiting? */
  260.  
  261.             if(State == STATE_Waiting)
  262.             {
  263.                 if(!CheckIO((struct IORequest *)TimeRequest))
  264.                     AbortIO((struct IORequest *)TimeRequest);
  265.  
  266.                 WaitIO((struct IORequest *)TimeRequest);
  267.             }
  268.  
  269.                 /* Start cleaning up... */
  270.  
  271.             DeleteMsgPort(AccountantPort);
  272.         }
  273.  
  274.         DeleteTimeRequest(TimeRequest,TimePort);
  275.     }
  276.  
  277.         /* Finished; wave goodbye and exit */
  278.  
  279.     Forbid();
  280.  
  281.     AccountantTask = NULL;
  282.     Signal((struct Task *)ThisProcess,SIG_HANDSHAKE);
  283. }
  284.  
  285. /****************************************************************************/
  286.  
  287.     /* DeleteAccountant():
  288.      *
  289.      *    Stop the accounting task.
  290.      */
  291.  
  292. VOID
  293. DeleteAccountant()
  294. {
  295.     ShakeHands(AccountantTask,SIG_KILL);
  296. }
  297.  
  298.     /* CreateAccountant():
  299.      *
  300.      *    Launch the accounting task.
  301.      */
  302.  
  303. BOOL
  304. CreateAccountant()
  305. {
  306.     InitSemaphore(&AccountantSemaphore);
  307.  
  308.     Forbid();
  309.  
  310.     if(LocalCreateTask("term Accountant Task",ThisProcess->pr_Task.tc_Node.ln_Pri + 10,(TASKENTRY)AccountantEntry,4096,0))
  311.         WaitForHandshake();
  312.  
  313.     Permit();
  314.  
  315.     return((BOOL)(AccountantTask != NULL));
  316. }
  317.  
  318. /****************************************************************************/
  319.  
  320.     /* QueryAccountantTime(struct timeval *Time):
  321.      *
  322.      *    Query the time that will have to elapse before
  323.      *    the next unit will begin.
  324.      */
  325.  
  326. ULONG
  327. QueryAccountantTime(struct timeval *Time)
  328. {
  329.     AccountantMsg Message;
  330.  
  331.     memset(&Message,0,sizeof(Message));
  332.  
  333.     Message.Message.mn_Length    = sizeof(Message);
  334.     Message.Type                = ACMSG_Query;
  335.  
  336.     SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  337.  
  338.     if(Time)
  339.     {
  340.         Time->tv_secs    = Message.Time.tv_secs;
  341.         Time->tv_micro    = Message.Time.tv_micro;
  342.     }
  343.  
  344.     return(Message.Time.tv_secs);
  345. }
  346.  
  347.     /* QueryAccountantCost():
  348.      *
  349.      *    Query the cost of the current connection.
  350.      */
  351.  
  352. ULONG
  353. QueryAccountantCost()
  354. {
  355.     ULONG Cost;
  356.  
  357.     SafeObtainSemaphoreShared(&AccountantSemaphore);
  358.     Cost = AccountantCost;
  359.     ReleaseSemaphore(&AccountantSemaphore);
  360.  
  361.     return(Cost);
  362. }
  363.  
  364.     /* StopAccountant():
  365.      *
  366.      *    Stop cost accounting and return the current
  367.      *    connection cost.
  368.      */
  369.  
  370. ULONG
  371. StopAccountant()
  372. {
  373.     AccountantMsg Message;
  374.     ULONG Cost;
  375.  
  376.     memset(&Message,0,sizeof(Message));
  377.  
  378.     Message.Message.mn_Length    = sizeof(Message);
  379.     Message.Type                = ACMSG_Stop;
  380.  
  381.     SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  382.  
  383.     SafeObtainSemaphoreShared(&AccountantSemaphore);
  384.     Cost = AccountantCost;
  385.     ReleaseSemaphore(&AccountantSemaphore);
  386.  
  387.     return(Cost);
  388. }
  389.  
  390.     /* StartAccountant(ULONG OnlineSeconds):
  391.      *
  392.      *    Start cost accounting for the current connection.
  393.      */
  394.  
  395. VOID
  396. StartAccountant(ULONG OnlineSeconds)
  397. {
  398.     PhoneEntry *ChosenEntry;
  399.     struct List *ChosenPattern;
  400.     struct timeval Remain;
  401.     struct timeval Now;
  402.     ULONG Cost;
  403.  
  404.     ChosenPattern = LockActivePattern();
  405.     ChosenEntry = LockActiveEntry(GlobalPhoneHandle);
  406.  
  407.         /* Forget the old data */
  408.  
  409.     StopAccountant();
  410.  
  411.         /* Now is the time for all good men... */
  412.  
  413.     GetSysTime(&Now);
  414.  
  415.         /* Did the connection happen a few seconds earlier? */
  416.  
  417.     if(OnlineSeconds > 0)
  418.     {
  419.         struct timeval Lead;
  420.  
  421.             /* Take care of the leading time */
  422.  
  423.         Lead.tv_secs    = OnlineSeconds;
  424.         Lead.tv_micro    = 0;
  425.  
  426.             /* Technically, the connection happened a bit earlier. */
  427.  
  428.         SubTime(&Now,&Lead);
  429.  
  430.             /* Choose the right rate accounting time. */
  431.  
  432.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  433.  
  434.             /* Enter the first rate. */
  435.  
  436.         Cost = PayPerUnit[DT_FIRST_UNIT];
  437.  
  438.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  439.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  440.  
  441.             /* Did the first unit elapse already? */
  442.  
  443.         while(-CmpTime(&Lead,&Remain) >= 0 && (Remain.tv_secs || Remain.tv_micro))    /* "Lead >= Remain" */
  444.         {
  445.                 /* Subtract the unit time */
  446.  
  447.             SubTime(&Lead,&Remain);
  448.  
  449.                 /* Update the "current" time so SelectTime */
  450.                 /* can do something sensible. */
  451.  
  452.             AddTime(&Now,&Remain);
  453.  
  454.             SelectTime(ChosenEntry,ChosenPattern,&Now);
  455.  
  456.             Remain.tv_secs    = SecPerUnit[DT_NEXT_UNIT] / 10000;
  457.             Remain.tv_micro    = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
  458.  
  459.                 /* In any case, a new unit starts here */
  460.  
  461.             Cost += PayPerUnit[DT_NEXT_UNIT];
  462.         }
  463.  
  464.             /* Adjust the remaining time it takes for the unit */
  465.             /* to elapse. */
  466.  
  467.         SubTime(&Remain,&Lead);
  468.     }
  469.     else
  470.     {
  471.             /* Choose the right rate accounting time. */
  472.  
  473.         SelectTime(ChosenEntry,ChosenPattern,&Now);
  474.  
  475.             /* Enter the first rate. */
  476.  
  477.         Cost = PayPerUnit[DT_FIRST_UNIT];
  478.  
  479.         Remain.tv_secs    = SecPerUnit[DT_FIRST_UNIT] / 10000;
  480.         Remain.tv_micro    = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
  481.     }
  482.  
  483.         /* Store the cost */
  484.  
  485.     ObtainSemaphore(&AccountantSemaphore);
  486.     AccountantCost = Cost;
  487.     ReleaseSemaphore(&AccountantSemaphore);
  488.  
  489.         /* That's it; "Remain" now holds the time to wait before */
  490.         /* the first unit elapses. Careful here, we must not */
  491.         /* launch a `zero' time wait request. */
  492.  
  493.     if(Remain.tv_secs > 0 || Remain.tv_micro > 0)
  494.     {
  495.         AccountantMsg Message;
  496.  
  497.         memset(&Message,0,sizeof(Message));
  498.  
  499.         Message.Message.mn_Length    = sizeof(Message);
  500.         Message.Type                = ACMSG_Start;
  501.         Message.Time                = Remain;
  502.  
  503.         SendMessageGetReply(AccountantPort,(struct Message *)&Message);
  504.     }
  505.  
  506.     UnlockActiveEntry(GlobalPhoneHandle);
  507.     UnlockActivePattern();
  508. }
  509.